#include "utils.h"
#include<iostream>
#include<cstdlib>
#include<assert.h>
#include<string>
#include<ctime>
#include <sys/epoll.h>

#include<unordered_map>

#include<chrono>
using namespace std;

class UdpServ{
   char m_ipstr[16];
   int m_port;
   int m_epfd;
   int m_lfd;
   unordered_map<string,user> umap;
   struct epoll_event m_evs[1024];

public:
   UdpServ(string ipstr, int port){
      strncpy(m_ipstr,ipstr.c_str(),ipstr.size());
      m_port =port;
      printf("%s\n""服务器配置完成");
   }
   void start(){
      m_lfd = SOCK_CREATE(SOCK_DGRAM,0);
      //SETNOBLK(m_lfd); // 设置非阻塞模式；
      if(m_lfd!=-1){
         SOCK_BIND(m_lfd,m_ipstr,m_port);
         printf("%s\n","服务器端绑定");
      }

   }

   bool epoll_init(){
      m_epfd = epoll_create(1024);
      if(m_epfd<0) return false;
      struct epoll_event ev;
	   ev.data.fd = m_lfd;
	   ev.events = EPOLLIN;
	   epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_lfd, &ev);
      return true;
      // 套接字描述符上树；
   }
   void epoll_loop(){
      struct sockaddr_in clientaddr;
      message mg;
      socklen_t clen = sizeof(clientaddr);
      auto lasttime = chrono::steady_clock::now();
      while(1){
      int nready = epoll_wait(m_epfd,m_evs,1024,0);
      if(nready<0){
                 perror("epoll wait error");
        if(errno==EINTR) {
            continue;
        }
        return;
      }
      if(0==nready){
         auto durt = chrono::steady_clock::now()-lasttime;
         if(durt>chrono::seconds(1800)){
            printf("%s\n","现已超时");
            break;
         }else{
            continue;
         }
      } 
      
      lasttime = chrono::steady_clock::now();
      for(int i=0;i<nready;++i){
       if (m_evs[i].events&EPOLLIN)
			{
				if (m_lfd != m_evs[i].data.fd)
					continue;
 
				int ret = recvfrom(m_lfd, (char *)&mg, 1040, 0, (sockaddr*)&clientaddr, &clen);
            // 接受并获取对端地址；
            if(ret>0){
               printf("%s\n",mg.fname);
            if(umap.empty()||umap.find(string(mg.fname))==umap.end()){
               char rebuf[64];
               memset(rebuf,0x00,64);
               if(umap.size()<100){
               user us;
               strncpy(us.nickname,mg.fname,7);
               us.addr  = clientaddr;
               us.lasttime = chrono::steady_clock::now();
               sprintf(rebuf,"登录成功");
               umap[mg.fname] = us;
               sendto(m_lfd,rebuf,strlen(rebuf),0,(sockaddr*)&clientaddr,clen);
               }
               
               else{
                  sprintf(rebuf,"服务器已满");
                  sendto(m_lfd,rebuf,strlen(rebuf),0,(sockaddr*)&clientaddr,clen);
               }
               // 登录新用户信息
            }
               if(strlen(mg.tname)==0){
                  //TODO
                  send2All(mg.fname,mg.m_msg);
               }else{
                  if(umap.find(mg.tname)==umap.end()){
                     send2One("",mg.fname,"此用户不存在");
                  }
                  send2One(mg.fname,mg.tname,mg.m_msg);
               }
            
            }
			}
      }
   }
           close(m_epfd);
        close(m_lfd);
   }

   void send2All(const char* fname,const char * msg){
      char sendbuf[1040];
      memset(sendbuf,0x00,1040);
      sprintf(sendbuf,"from %s::%s",fname,msg);
      printf("%s\n",sendbuf);

      for(auto &v:umap){
         auto durt = chrono::steady_clock::now()-v.second.lasttime;
         if(durt<chrono::seconds(300)){

         sendto(m_lfd,sendbuf,strlen(sendbuf),0,(struct sockaddr*)&(v.second.addr),sizeof(v.second.addr));
      }
      }

   }

   void send2One(const char*fname,const char* tname,const char *msg){
      char sendbuf[1040];
      memset(sendbuf,0x00,1040);
      if(strlen(fname)==0){
      sprintf(sendbuf,"warning::%s",msg);
      }
      else{
         sprintf(sendbuf,"from %s::%s",fname,msg);
      }
      printf("%s\n",sendbuf);
      sendto(m_lfd,sendbuf,strlen(sendbuf),0,(struct sockaddr*)&(umap[tname].addr),sizeof(umap[tname].addr));
   }
};

int main(){
   UdpServ userv("127.0.0.1",8888);
   userv.start();
   if(!userv.epoll_init()){
      printf("成功");
   }
   userv.epoll_loop();
}